﻿using System;
using System.IO;
using System.Net.Sockets;
using NewLife.Net.Sockets;

namespace NewLife.Net.IO
{
    /// <summary>文件服务端</summary>
    public class FileServer : NetServer<FileSession>
    {
        #region 属性
        private String _SavedPath;
        /// <summary>保存路径</summary>
        public String SavedPath { get { return _SavedPath ?? (_SavedPath = "Data"); } set { _SavedPath = value; } }
        #endregion

        #region 方法
        /// <summary>实例化一个文件服务</summary>
        public FileServer()
        {
            Port = 33;
            ProtocolType = NetType.Tcp;

            Name = "文件服务";
        }

        ///// <summary>附加服务器</summary>
        ///// <param name="server"></param>
        ///// <returns></returns>
        //public override Boolean AttachServer(ISocketServer server)
        //{
        //    // 接收文件需要顺序
        //    if (server is TcpServer)
        //    {
        //        var tcp = server as TcpServer;
        //        tcp.ProcessAsync = false;
        //        // 连续传输文件，间隔不得超过5秒
        //        //tcp.SessionTimeout = 5;
        //    }
        //    return base.AttachServer(server);
        //}
        #endregion

        #region 事件
        ///// <summary>收到连接时</summary>
        ///// <param name="session"></param>
        //protected override void OnNewSession(ISocketSession session)
        //{
        //    base.OnNewSession(session);

        //    session.Received += (sender, e) =>
        //    {
        //        var tc = sender as ISocketSession;

        //        var stream = tc.Stream;
        //        stream.Write(e.Data, 0, e.Length);
        //        stream.Seek(-1 * e.Length, SeekOrigin.Current);

        //        // 数据太少时等下一次，不过基本上不可能。5是FileFormat可能的最小长度
        //        if (stream.Length < 5) return;

        //        var format = FileFormat.Load(stream);
        //    };
        //}
        #endregion
    }

    /// <summary>文件服务会话</summary>
    public class FileSession : NetSession<FileServer>
    {
        #region 属性
        private FileFormat _Inf;
        /// <summary>文件信息</summary>
        public FileFormat Inf { get { return _Inf; } set { _Inf = value; } }

        private Int64 _Length;
        /// <summary>长度</summary>
        public Int64 Length { get { return _Length; } set { _Length = value; } }

        private FileStream _Stream;
        /// <summary>文件流</summary>
        public FileStream Stream { get { return _Stream; } set { _Stream = value; } }

        private DateTime _StartTime;
        /// <summary>开始时间</summary>
        public DateTime StartTime { get { return _StartTime; } set { _StartTime = value; } }
        #endregion

        #region 构造
        //public FileSession()
        //{
        //}

        ///// <summary>开始</summary>
        //public override void Start()
        //{
        //    base.Start();
        //    StartTime = Session.StartTime;
        //}

        /// <summary>销毁会话</summary>
        /// <param name="disposing"></param>
        protected override void Dispose(Boolean disposing)
        {
            base.Dispose(disposing);

            CloseStream();

            if (Inf != null)
            {
                WriteError("{0}接收失败，完成度{1:p}", Inf.Name, (Double)Length / Inf.Length);
            }
        }

        void CloseStream()
        {
            if (Stream != null)
            {
                try
                {
                    Stream.Flush();
                    Stream.SetLength(Stream.Position);
                    Stream.Dispose();
                }
                catch { }
                Stream = null;
            }
        }
        #endregion

        /// <summary>处理收到的数据</summary>
        /// <param name="e"></param>
        protected override void OnReceive(ReceivedEventArgs e)
        {
            //base.OnReceive(e);

            var stream = e.Packet.GetStream();

            // 第一个数据包解析头部
            if (e.Packet.Total > 0 && Inf == null)
            {
                var fi = new FileFormat();
                try
                {
                    fi.Read(stream);

                    if (fi.Checksum != fi.Crc)
                        throw new XException("文件{0}校验和错误{1:X8}!={2:X8}！", fi.Name, fi.Checksum, fi.Crc);
                }
                catch (Exception ex)
                {
                    WriteError("无法解析文件头！{0}", ex.Message);
                    // 如果加载失败，则关闭会话
                    Dispose();
                    return;
                }
                Inf = fi;
                Length = 0;
                if (StartTime == DateTime.MinValue) StartTime = DateTime.Now;

                // 加大网络缓冲区
                Session.Client.ReceiveBufferSize = 8 * 1024 * 1024;

                var file = Host.SavedPath.CombinePath(Inf.Name).EnsureDirectory();
                Stream = file.AsFile().OpenWrite();
                WriteLog("接收{0}，{1:n0}kb", Inf.Name, Inf.Length / 1024);

                if (stream.Position >= stream.Length) return;
            }

            var len = stream.Length - stream.Position;
            if (len > 0)
            {
                Length += len;
                var ms = (DateTime.Now - StartTime).TotalMilliseconds;
                var speed = Length / 1024 / ms * 1000;
                WriteLog("收到{0:n0}字节，{1:n0}kb/s，完成{2:p}", len, (Int32)speed, (Double)Length / Inf.Length);
                if (Stream != null && Stream.CanWrite) stream.CopyTo(Stream);
                //}
                //else
                //{
                if (Length >= Inf.Length)
                {
                    //var ms = (DateTime.Now - StartTime).TotalMilliseconds;
                    //var speed = Length / 1024 / ms * 1000;
                    WriteLog("{0}接收完成，{1:n0}ms，{2:n0}kb/s", Inf.Name, (Int32)ms, (Int32)speed);
                    //Dispose();

                    // 清空，方便接收下一个文件
                    Inf = null;
                    CloseStream();
                    StartTime = DateTime.MinValue;
                }
            }
        }
    }
}